/**
 * 
 */
package idv.takeshi.software.productline.bookstore.application.service.spring;

import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import idv.takeshi.software.productline.bookstore.application.service.CanNotRemoveCategoryException;
import idv.takeshi.software.productline.bookstore.application.service.MaintainCategoriesService;
import idv.takeshi.software.productline.bookstore.domain.model.category.BookAlreadyAttachedException;
import idv.takeshi.software.productline.bookstore.domain.model.category.Category;
import idv.takeshi.software.productline.bookstore.domain.model.category.CategoryException;
import idv.takeshi.software.productline.bookstore.domain.model.category.CategoryFactory;
import idv.takeshi.software.productline.bookstore.domain.model.category.CategoryRepository;
import idv.takeshi.software.productline.bookstore.domain.model.valueobject.InternationalText;

/**
 * A service implementation for MaintainCategoriesService.
 * @author takeshi
 *
 */
@Service("maintainCategoriesService")
@Transactional
public class MaintainCategoriesServiceImpl implements MaintainCategoriesService {
	
	private static final Log log = LogFactory.getLog(MaintainCategoriesServiceImpl.class);
	
	@Autowired
	private CategoryRepository categoryRepository;
	
	@Autowired
	private CategoryFactory categoryFactory;
	
	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.application.service.MaintainCategoriesService#getRootCategory()
	 */
	public Category getRootCategory() {
		return this.categoryRepository.findRoot();
	}

	
	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.application.service.MaintainCategoriesService#addNewCategory(java.lang.Long, idv.takeshi.software.productline.bookstore.domain.model.valueobject.InternationalText)
	 */
	public Long addNewCategory(Long parentCategoryId, InternationalText iText) throws CategoryException {
		Validate.notNull(parentCategoryId, "parentCategoryId shall always not be null");
		Validate.notNull(iText, "iText shall always not be null");
		
		Category parentCategory = null;
		Category category = null;
		parentCategory = this.categoryRepository.findById(parentCategoryId);
		category = this.categoryFactory.newInstance(iText);
		this.categoryRepository.add(category);
		parentCategory.addSubCategory(category);
		
		return category.getId();
	}

	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.application.service.MaintainCategoriesService#moveCategory(java.lang.Long, java.lang.Long, int)
	 */
	public void moveCategory(Long parentCategoryId, Long categoryId,
			int newSequence)throws BookAlreadyAttachedException {
		Validate.notNull(parentCategoryId, "parentCategoryId shall always not be null");
		Validate.notNull(categoryId, "categoryId shall always not be null");
		
		Category rootCategory = this.categoryRepository.findRoot();
		Category oldParentCategory = null;
		Category newParentCategory = null;
		Category category = null;
		Set<Category> superCategories = null;
		
		
		category = rootCategory.getAllSubCategory(categoryId);
		newParentCategory = rootCategory.getAllSubCategory(parentCategoryId);
		
		superCategories = rootCategory.getSuperCategories(category); 
		oldParentCategory = (Category) CollectionUtils.get(superCategories, superCategories.size() - 1);
		oldParentCategory.removeSubCategory(category);
		try{
			newParentCategory.addSubCategory(category, newSequence);
		} catch(BookAlreadyAttachedException e){
			String msg = "add sub-category:" + category.getId() + " to parent category:" + 
				newParentCategory.getId() + " failed";
			log.warn(msg);
			throw e;
		}
	}

	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.application.service.MaintainCategoriesService#removeCategory(java.lang.Long)
	 */
	public void removeCategory(Long categoryId)
			throws CanNotRemoveCategoryException {
		Validate.notNull(categoryId, "categoryId shall always not be null");
		
		Category rootCategory = this.categoryRepository.findRoot();
		Category category = null;
		Category parentCategory = null;
		Set<Category> superCategories = null;
		category = rootCategory.getAllSubCategory(categoryId);
		if(category.isRoot()){
			throw new CanNotRemoveCategoryException("category:" + categoryId + " is root, can not be removed");
		}
		
		if(category.hasAllBooks()){
			throw new CanNotRemoveCategoryException("category:" + categoryId + " has descendant books" + 
					", can not be removed");
		}
			
		superCategories = rootCategory.getSuperCategories(category);
		parentCategory = (Category) CollectionUtils.get(superCategories, superCategories.size() - 1);
		
		parentCategory.removeSubCategory(category);
		for(Category tmp : category.getAllSubCategories())
			this.categoryRepository.remove(tmp);
	}

	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.application.service.MaintainCategoriesService#updateCategory(java.lang.Long, idv.takeshi.software.productline.bookstore.domain.model.valueobject.InternationalText)
	 */
	public void updateCategory(Long categoryId, InternationalText newIText) {
		Validate.notNull(categoryId, "categoryId shall always not be null");
		Validate.notNull(newIText, "newIText shall always not be null");
		Category category = this.categoryRepository.findById(categoryId);
		category.setName(newIText);
	}

}
